# _*_ coding:utf-8 _*_

import sys
import pandas as pd
import re
from pandas import DataFrame
import itertools
from collections import Iterable
from SR_Context import SR_Context
import StrUtils
import SQL_Utils
import CommonUtils
import ImportCode

logger = CommonUtils.getLogger('PandasUtils')
list_type = (int, float, str)


def justProcessMacros(_src, statement):

    def lambda_match(_src, replacements):
        varName = replacements.pop(0)
        if CommonUtils.getDict(varName, _src.get_value('require'), None) != None:
            return '_src.get_value("require")["' + varName + '"]'
        return ('df["' + varName + '"]')

    reg_str = r'\$\.\w*'
    status, s = StrUtils.getStatementContent(statement, separator='//')
    if (status != 0):
        return 1, 'ERROR:【' + mark + ': ' + s + '】 ' + statement

    statement = s
    status, statement = StrUtils.getStatementContent(statement, separator=';')

    all_labels = re.findall(reg_str, statement)

    # Define list of replacements which matches all_labels *IN ORDER*
    replacements = []
    for label in all_labels: # keep in order! this is important
        replacements.append(label[2:])

    # Perform replacements in order
    transform_newmaps = re.sub(reg_str, lambda match: lambda_match(_src, replacements), statement)

    new_statement = transform_newmaps.replace('df[""]', 'sr.data')
    return 0, new_statement


def checkGroupDict(_src, dict, rightIsExpr):
    for key, value in dict.items():
        # 分组条件构造器dict的key不一定只是一个标识符，它还可能是一个完整的python表达式，如：a+b
        #if key.isidentifier():
        status, s = justProcessMacros(_src, key)
        if (status != 0):
            return False

        status, s = ImportCode.sr_CheckCode(_src, s)
        if (status == 0):
            if rightIsExpr:
                #检查value是个合法的表达式
                status, s = justProcessMacros(_src, value)
                if (status != 0):
                    return False

                status, s = ImportCode.sr_CheckCode(_src, s)
                if (status == 0):
                    return True
            else:
                #检查value是个合法的值
                result = all(any([isinstance(word, type_) for type_ in list_type]) for word in value)
                if not result:
                    return False
        else:
            return False
    return True


def convertList2Dict(lst):
    res_dct = {}
    num = 0
    for x in lst:
        if (isinstance(x, Iterable) and (not isinstance(x, str))):
            for it in x:
                if (isinstance(it, Iterable) and (not isinstance(it, str))):
                    return None
                else:
                    res_dct[it] = num
        else:
            res_dct[x] = num
        num += 1
    return res_dct


def itemCmp(item0, item1, len_levels):
    for i in range(len_levels):
        if item0[i] != item1[i]:
            return i
    return i+1


def getActionStatement(_src, x_y, item_s, actionStatement=''):
    if (x_y == 'y'):
        ivar = _src.numY
        _src.numY = ivar + 1
        _src.titlesY[ivar] = item_s
        actionStatement = 'cu.stateY(_src,' + str(ivar) + ')'
    else:
        ivar = _src.numX
        _src.numX = ivar + 1
        _src.titlesX[ivar] = item_s

        if (actionStatement != ''):
            # avg({x}, expr),  动作表达式用{x}代表当前行
            actionStatement = actionStatement.format(x=str(ivar))
        else:
            action = _src.get_value('action', defValue='')
            if (action):
                actionStatement = 'cu.actionX(' + str(ivar) + ', ' + action + ')'
            else:
                logger.error('动作表达式未正确设置，请通过“$action=表达式”设置')
                return 1, ' $!ERROR!$ '

    return 0, actionStatement


def multiIndex2Statements(_src, x_y, indent, item_s, colNames, levels, rightIsExpr=False, actionStatement=''):
    '''
    colNames like {'key1' : ['1', '2', '3'], 'key2' : ['4', '5', '6'], 'key3' : ['7', '8', '9']}
    经本函数处理，生成的结果如下：
    key1 == r"""1""":
        key2 == r"""4""":
            key3 == r"""7""": actionExpr

    levels中的内容，可能是"val//文字说明"的形式. 如：'key1' : ['01//汉族', '02//回族', '03//满族']
    '''
    retStr = ''

    lastItem = ()
    len_levels = len(levels)
    for item in itertools.product(*levels):

        item_s_ = item_s[:-2]

        #这里的"."是临时占一个位置，//后面的内容是为了形成title用的
        for x in item:
            status, str_ = StrUtils.getStatementContent(str(x), True, '')
            item_s_.append('.//' + str_)

        status, actionStatement = getActionStatement(_src, x_y, item_s_, actionStatement=actionStatement)
        if (status != 0):
            return 2, ''

        i_pre = itemCmp(lastItem, item, len_levels)
        lastItem = item
        len0 = len(item) - i_pre - 1
        for i in range(len0):
            if rightIsExpr:
                new_statement = str(colNames[i]) + ' == ' + StrUtils.getStatementContent(str(item[i]))[1]
            else:
                if isinstance(item[i], str):
                    str_ = StrUtils.safePythonStr(StrUtils.getStatementContent(str(item[i]))[1])
                    new_statement = str(colNames[i]) + ' == r"""' + str_ + '"""'
                else:
                    new_statement = str(colNames[i]) + ' == ' + StrUtils.getStatementContent(str(item[i]))[1]

            retStr += indent + '    ' * (i_pre+i)  + 'if ' + new_statement + ':\n'

        if rightIsExpr:
            status, str_ = StrUtils.getStatementContent(str(item[i_pre+len0]))
            new_statement = str(colNames[i_pre+len0]) + ' == ' + str_
        else:
            if isinstance(item[i_pre+len0], str):
                str_ = StrUtils.safePythonStr(StrUtils.getStatementContent(str(item[i_pre+len0]))[1])
                new_statement = str(colNames[i_pre+len0]) + ' == r"""' + str_ + '"""'
            else:
                new_statement = str(colNames[i_pre+len0]) + ' == ' + str(item[i_pre+len0])

        actionIdents = '    ' * (i_pre+len0+1)
        actionStatement = ('\n' + actionIdents).join(actionStatement.split('\n'))

        retStr += indent + '    ' * (i_pre+len0) + 'if ' + new_statement + ':\n' + indent + actionIdents + actionStatement + '\n'

    return 0, retStr


def dict2Statements(_src, indent, item_s, dict_, actionStatement='', key_or_range='k'):
    '''
    dict like {'key1' : ['1', '2', '3']}
    经本函数处理，生成的结果如下：
    pos=getPos(key1, {'1':0, '2':1, '3':2}), if (pos >= 0): actionExpr

    if (getDictPos({'1':0, '2':1, '3':2}, '1') >= 0): print ('haha')
    value中的内容，可能是"val//文字说明"的形式. 如：'key1' : ['01//汉族', '02//回族', '03//满族']
    '''
    retStr = ''

    key_ = next(iter(dict_))
    value_ = dict_[key_]
    if isinstance(value_, str) or not isinstance(value_, Iterable):
        value_ = [value_]

    if (key_or_range == 'k'):
        val_dict = {}
    else:
        val_dict = []
    pos = 0
    for val_ in sorted(value_):
        item_s_ = item_s[:-2]

        #这里的"."是临时占一个位置，//后面的内容是为了形成title用的
        if isinstance(val_, str):
            statement = StrUtils.getStatementContent(val_, False, '')[1]
            item_s_.append('.//'+StrUtils.getStatementContent(val_, True, '')[1])
        else:
            statement = val_
            item_s_.append('.//'+str(val_))

        # 这里调用获取动作表达式的函数，是为了设置一些变量的值，得到的动作表达式本身没意义
        status, s = getActionStatement(_src, 'y', item_s_)
        if (status != 0):
            return 2, ''

        if (key_or_range == 'k'):
            val_dict[statement] = pos
        else:
            val_dict.append(statement)
        pos += 1

    val_s = str(val_dict)

    status, key_ = justProcessMacros(_src, key_)
    if (key_or_range == 'k'):
        new_statement = '_p_o_s__ = cu.getDictPos(' + key_ + ',' + str(_src.numY-len(value_)) + ',' + val_s + ')'
    else:
        new_statement = '_p_o_s__ = cu.sortKeysList(' + key_ + ',' + str(_src.numY-len(value_)) + ',' + val_s + ')'

    if (actionStatement == ''):
        actionStatement = 'cu.stateY(_src, _p_o_s__)'
    else:
        actionStatement = '\n    '.join(actionStatement.split('\n'))

    retStr += indent + new_statement + '\n' + indent + 'if (_p_o_s__ >= 0):\n' + indent + '    ' + actionStatement + '\n'

    return 0, retStr


def dictOrRule2Statements(_src, x_y, indent='', item_s=[], dictExpr='', forceStr=True, rightIsExpr=False, actionStatement='', key_or_range='k'):
    if (not dictExpr):
        return 1, ''

    if isinstance(dictExpr, dict):
        '''
        dictExpr like {'key1' : ['1', '2', '3'], 'key2' : ['4', '5', '6'], 'key3' : ['7', '8', '9']}
        '''
        if ((len(dictExpr) == 1) and (x_y == 'y') and (not rightIsExpr)):
            if checkGroupDict(_src, dictExpr, rightIsExpr):
                status, retStr = dict2Statements(_src, indent, item_s, dictExpr, key_or_range=key_or_range, actionStatement=actionStatement)
                if (status == 0):
                    return 0, retStr

        if (key_or_range == 'r'):
            return 2, ('分组（range）描述错：' + str(dictExpr))

        #colNames是个字典，把它的所有key放到colNames_，所有value放到levels
        colNames_ = []
        for key, value in dictExpr.items():
            status, s = justProcessMacros(_src, key)
            colNames_.append(s)

        levels = []
        for key, value in dictExpr.items():
            if isinstance(value, (list, tuple)):
                levels.append(value)
            else:
                levels.append([value])

        status, retStr = multiIndex2Statements(_src, x_y, indent, item_s, colNames_, levels, rightIsExpr=rightIsExpr, actionStatement=actionStatement)
        return status, retStr

    return 3, ('分组（dict）描述错：' + str(dictExpr))


def index2Statements(_src, x_y, indent='', item_s=[], colNames='', actionStatement=''):
    df = _src.df
    if StrUtils.isEmpty(df):
        if _src.cursor:
            if not _src.gb_table:
                if not _src.cur_sql:
                    sql = _src.get_value('sql')
                if not sql:
                    return 5, ''

                try:
                    gb_table = SQL_Utils.extract_tables(sql)
                    _src.gb_table = gb_table[0]
                except:
                    return 5, ''

            sql = 'SELECT ' + colNames + ' FROM ' + _src.gb_table + ' GROUP BY ' + colNames
            try:
                _src.cursor.execute(sql)
                colNames = colNames.split(',')
                df = DataFrame(_src.cursor.fetchall(), columns=colNames)
            except:
                logger.error('分组（groupby）字段列表描述错：' + colNames)
                info = sys.exc_info()
                return 4, info[1]
    else:
        '''
        colNames 数据示例： ['类目','月份']
        '''
        colNames = colNames.split(',')

    if StrUtils.isEmpty(df):
        logger.error('无法获得数据分组用Dataframe(df)：' + str(colNames))
        return 1, ''

    try:
        df_tmp = df.groupby(colNames).agg(name=(colNames[0], "count"))
    except:
        return 1, ''

    if type(df_tmp.index) == pd.core.indexes.multi.MultiIndex:
        levels = df_tmp.index.levels
        status, retStr = multiIndex2Statements(_src, x_y, indent, item_s, colNames, levels, actionStatement)
        return status, retStr
    else:
        retStr = ''
        # type(df_tmp.index) == pd.core.indexes.base.Index:
        # pandas.core.indexes.numeric.Int64Index
        if (x_y == 'y'):
            colNames = {'df["' + str(colNames[0]) + '"]' : df_tmp.index}
            status, retStr = dict2Statements(_src, indent, item_s, colNames, actionStatement=actionStatement)
            if (status == 0):
                return 0, retStr

        for i in range(len(df_tmp.index)):
            if isinstance(df_tmp.index[i], str):
                str_ = StrUtils.safePythonStr(df_tmp.index[i])
                new_statement = 'df["' + str(colNames[0]) + '"] == r"""' + str_  + '"""'
            else:
                new_statement = 'df["' + str(colNames[0]) + '"] == ' + str(df_tmp.index[i])

            # './/' + , 这里的.相当于是表达式，//后是注释，如果表达式部分没内容，可能在取表达式或注释时出错
            item_s_ = item_s[:-2]
            item_s_.append('.//' + str(df_tmp.index[i]))
            status, actionStatement = getActionStatement(_src, x_y, item_s_, actionStatement)
            if (status != 0):
                return 3, ''
            else:
                actionStatement = '\n    '.join(actionStatement.split('\n'))

            retStr += (indent + 'if ' + new_statement + ':\n' + indent + '    ' + actionStatement) + '\n'

    return 0, retStr
